package csl.blog.server.config.redis;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author chensl
 * @date 2023-05-11 17:34:58
 * @description
 */
@Component
public class RedisLocker implements IDistributedLocker {

    private final static String LOCK_KEY_PREFIX = "r_lock:";

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public <T> T lock(T key, IWorker<T> worker) throws Exception {
        return lock(key, worker, 100);
    }

    @Override
    public <T> T lock(T key, IWorker<T> worker,int lockTime) throws Exception {
        return lock(key, worker, 100, false);
    }

    @Override
    public <T> T lock(T key, IWorker<T> worker, int lockTime, boolean fair) throws Exception {
        RLock lock = fair ? redissonClient.getFairLock(LOCK_KEY_PREFIX + key) : redissonClient.getLock(LOCK_KEY_PREFIX + key);
        boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
        if (success) {
            try {
                return worker.invoke();
            } finally {
                lock.unlock();
            }
        }
        throw new RuntimeException();
    }

    @Override
    public <T> T lock(List<T> keys, IWorker<T> worker) throws Exception {
        return lock(keys, worker, 100);
    }

    @Override
    public <T> T lock(List<T> keys, IWorker<T> worker, int lockTime) throws Exception {
        RLock[] rLocks = new RLock[keys.size()];
        for (int i = 0; i < keys.size(); i++) {
            T key = keys.get(i);
            rLocks[i] = redissonClient.getLock(LOCK_KEY_PREFIX + key);
        }
        RLock rLock = redissonClient.getMultiLock(rLocks);
        boolean success = rLock.tryLock(100, lockTime, TimeUnit.SECONDS);
        if (success) {
            try {
                return worker.invoke();
            } finally {
                rLock.unlock();
            }
        }
        throw new RuntimeException();
    }

}
